home *** CD-ROM | disk | FTP | other *** search
/ PC-X 1997 October / pcx14_9710.iso / swag / strings.swg / 0138_Fast Searching for a String.pas < prev   
Encoding:
Pascal/Delphi Source File  |  1997-08-30  |  6.6 KB  |  176 lines

  1.  
  2. {$R-,S+,I-,D-,T-,F-,V-,B-,N-}
  3.  
  4. unit Searches;
  5.  
  6. { A unit for rapidly searching a buffer for a string.
  7.  
  8.   Version 1.00 - 10/26/1987 - First general release
  9.  
  10.   Scott Bussinger
  11.   Professional Practice Systems
  12.   110 South 131st Street
  13.   Tacoma, WA  98444
  14.   (206)531-8944
  15.   Compuserve 72247,2671
  16.  
  17.   BlockPos was originally written by Randy Forgaard for use with Turbo
  18.   Pascal version 3.0.
  19.  
  20.   The Boyer-Moore routines were originally written by Van Hall for Turbo
  21.   Pascal version 3.0 and have been extensively rearranged for optimum use
  22.   with Turbo Pascal 4.0.  Note that the Boyer-Moore routines are MUCH, MUCH
  23.   slower than using BlockPos (which is written with inline code). }
  24.  
  25.  
  26. interface
  27.  
  28. function BlockPos(var Buffer;Size: word;S: string): integer;
  29.   { Search in Buffer of Size bytes for the string S }
  30.  
  31. type BoyerTable = record
  32.        Match: string;
  33.        MatchLength: byte;
  34.        Table: array[char] of byte
  35.        end;
  36.  
  37. procedure MakeBoyerTable(MatchString: string;var Table: BoyerTable);
  38.   { Generate the necessary table for doing a Boyer-Moore search }
  39.  
  40. function BoyerMoore(var BufferAddr;Size: word;Start: word;var Table: BoyerTable): word;
  41.   { Search a Buffer of Size characters beginning at Start for the match string defined in Table }
  42.  
  43.  
  44. implementation
  45.  
  46. function BlockPos(var Buffer;Size: word;S: string): integer;
  47.   { Search in Buffer of Size bytes for the string S }
  48.   begin
  49.   { Load "buffer" address into ES:DI, "buffer" offset into BX, Length(s) -
  50.     1 into DX, contents of "s[1]" into AL, offset of "s[2]" into SI, and
  51.     "size" - Length(s) + 1 into CX.  If "size" < Length(s), or if
  52.     Length(s) = 0, return zero. }
  53.  
  54.   Inline($1E/               {        PUSH    DS           }
  55.          $16/               {        PUSH    SS           }
  56.          $1F/               {        POP     DS           }
  57.          $C4/$BE/>buffer/   {        LES     DI,buffer[BP]}
  58.          $89/$FB/           {        MOV     BX,DI        }
  59.          $8B/$8E/>size/     {        MOV     CX,size[bp]  }
  60.          $8D/$B6/>s+2/      {        LEA     SI,s+2[bp]   }
  61.          $8A/$86/>s+1/      {        MOV     AL,s+1[bp]   }
  62.          $8A/$96/>s/        {        MOV     DL,s[bp]     }
  63.          $84/$D2/           {        TEST    DL,DL        }
  64.          $74/$23/           {        JZ      ERROR        }
  65.          $FE/$CA/           {        DEC     DL           }
  66.          $30/$F6/           {        XOR     DH,DH        }
  67.          $29/$D1/           {        SUB     CX,DX        }
  68.          $76/$1B/           {        JBE     ERROR        }
  69.  
  70.   { Scan the ES:DI buffer, looking for the first occurrence of "s[1]."  If
  71.     not found prior to reaching Length(s) characters before the end of the
  72.     buffer, return zero.  If Length(s) = 1, the entire string has been
  73.     found, so report success. }
  74.  
  75.        $FC/               {        CLD                  }
  76.        $F2/               {NEXT:   REPNE                }
  77.        $AE/               {        SCASB                }
  78.        $75/$16/           {        JNE     ERROR        }
  79.        $85/$D2/           {        TEST    DX,DX        }
  80.        $74/$0C/           {        JZ      FOUND        }
  81.  
  82.   { Compare "s" (which is at SS:SI) with the ES:DI buffer, in both cases
  83.     starting with the first byte just past the length byte of the string.
  84.     If "s" does not match what is at the DI position of the buffer, reset
  85.     the registers to the values they had just prior to the comparison, and
  86.     look again for the next occurrence of the length byte. }
  87.  
  88.          $51/               {        PUSH    CX           }
  89.          $57/               {        PUSH    DI           }
  90.          $56/               {        PUSH    SI           }
  91.          $89/$D1/           {        MOV     CX,DX        }
  92.          $F3/               {        REPE                 }
  93.          $A6/               {        CMPSB                }
  94.          $5E/               {        POP     SI           }
  95.          $5F/               {        POP     DI           }
  96.          $59/               {        POP     CX           }
  97.          $75/$EC/           {        JNE     NEXT         }
  98.  
  99.   { String found in buffer.  Set AX to the offset, within buffer, of the
  100.     first byte of the string (the length byte), assuming that the first
  101.     byte of the buffer is at offset 1. }
  102.  
  103.          $89/$F8/           {FOUND:  MOV     AX,DI        }
  104.          $29/$D8/           {        SUB     AX,BX        }
  105.          $EB/$02/           {        JMP     SHORT RETURN }
  106.  
  107.   { An "error" condition.  Return zero. }
  108.  
  109.          $31/$C0/           {ERROR:  XOR     AX,AX        }
  110.          $89/$46/$FE/       {RETURN: MOV     [BP-2],AX    }
  111.          $1F)               {        POP     DS           }
  112.   end;
  113.  
  114. procedure MakeBoyerTable(MatchString: string;var Table: BoyerTable);
  115.   { Generate the necessary table for doing a Boyer-Moore search }
  116.   var Counter: byte;
  117.   begin
  118.   with Table do
  119.     begin
  120.     Match := MatchString;
  121.     MatchLength := length(MatchString);
  122.     fillChar(Table,sizeof(Table),MatchLength);
  123.     if MatchLength > 0 then
  124.       for Counter := pred(MatchLength) downto 1 do
  125.         if Table[Match[Counter]] = MatchLength then
  126.             Table[Match[Counter]] := MatchLength-Counter
  127.     end
  128.   end;
  129.  
  130. function BoyerMoore(var BufferAddr;Size: word;Start: word;var Table: BoyerTable): word;
  131.   { Search a Buffer of Size characters beginning at Start for the match string defined in Table }
  132.   type Ptr = record
  133.          case integer of
  134.            0: (Ptr: ^char);
  135.            1: (Offset: word;
  136.                Segment: word)
  137.          end;
  138.   var Buffer: array[1..$FFF1] of char absolute BufferAddr;
  139.       BufferPtr: Ptr;
  140.       BufferEndOfs: word;
  141.       MatchPtr: Ptr;
  142.       MatchEndPtr: Ptr;
  143.   begin
  144.   with Table do
  145.     if MatchLength = 0                           { Are we looking for an empty string? }
  146.      then
  147.       BoyerMoore := 0
  148.      else
  149.       begin
  150.       MatchEndPtr.Ptr := @Match[MatchLength];
  151.       MatchPtr := MatchEndPtr;
  152.       BufferPtr.Ptr := @Buffer[pred(Start+MatchLength)];
  153.       BufferEndOfs := ofs(Buffer[Size]);
  154.       repeat
  155.         if BufferPtr.Ptr^ = MatchPtr.Ptr^
  156.          then
  157.           begin
  158.           dec(BufferPtr.Offset);
  159.           dec(MatchPtr.Offset)
  160.           end
  161.          else
  162.           begin
  163.           MatchPtr := MatchEndPtr;
  164.           inc(BufferPtr.Offset,Table[BufferPtr.Ptr^])
  165.           end
  166.       until (MatchPtr.Ptr=@Match) or (ofs(BufferPtr.Ptr^)>=BufferEndOfs);
  167.       if MatchPtr.Ptr = @Match
  168.        then
  169.         BoyerMoore := ofs(BufferPtr.Ptr^) - ofs(Buffer) + 2
  170.        else
  171.         BoyerMoore := 0
  172.       end
  173.   end;
  174.  
  175. end.
  176.